﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Ports;
using System.IO;
using System.Globalization;

namespace FunctionalLayer
{
    /// <summary>
    /// This class is to read the Arduino 
    /// </summary>
    public class Arduino
    {
        #region Fields
        private double _amperage;
        /// <summary>
        /// The ampere the engine uses
        /// </summary>
        public double Amperage
        {
            get { return _amperage; }
            set { _amperage = value; }
        }
        private double _distance;
        /// <summary>
        /// The total distance that the car drived
        /// </summary>
        public double Distance
        {
            get { return _distance; }
            set { _distance = value; }
        }
        private double _speed;
        /// <summary>
        /// The speed of the car
        /// </summary>
        public double Speed
        {
            get { return _speed; }
            set { _speed = value; }
        }
        private double _voltage;
        /// <summary>
        /// The voltage the engine uses
        /// </summary>
        public double Voltage
        {
            get { return _voltage; }
            set { _voltage = value; }
        }
        private double _wattage;
        /// <summary>
        /// The watt that is used 
        /// </summary>
        public double Wattage
        {
            get { return _wattage; }
            set { _wattage = value; }
        }
        private double _temperature;
        /// <summary>
        /// The temperature of the engine
        /// </summary>
        public double Temperature
        {
            get { return _temperature; }
            set { _temperature = value; }
        }
        private int _maxTemperature;
        private bool _overheat;
        /// <summary>
        /// Determines if the engine is overheated
        /// </summary>
        public bool Overheat
        {
            get { return _overheat; }
            set { _overheat = value; }
        }

        private SpeedSensor _speedSensor;
        private WattSensor _wattSensor;

        private SerialPort _serialPort;
        private bool _initialised;
        #endregion

        /// <summary>
        /// This is for noticing CarController that there is new data to show
        /// </summary>
        public event Action EventNewData;

        /// <summary>
        /// Is fired in case of entering a new section
        /// </summary>
        public event Action OnNewSection;

        public event Action<string> ExceptionOccured;

        //Instance of class
        private static Arduino _instance;

        /// <summary>
        /// Singleton - Returns instance of class
        /// </summary>
        public static Arduino GetInstance
        {
            get
            {
                if (_instance == null)
                {
                    _instance = new Arduino();
                }
                return _instance;
            }
        }

        /// <summary>
        /// Initializes the objects needed
        /// </summary>
        public void Init()
        {
            _speedSensor = new SpeedSensor(3);
            _speedSensor.NewSection += _speedSensor_NewSection;
            _wattSensor = new WattSensor();
            _serialPort = new SerialPort(ConfigSingleton.Settings.ArduinoPort, 115200, Parity.None, 8, StopBits.One);
            _serialPort.DataReceived += _serialPort_DataReceived;
            _maxTemperature = 70;
            OpenPort();
            _initialised = true;
        }

        /// <summary>
        /// Event handler for new section event of the speed sensor
        /// </summary>
        void _speedSensor_NewSection()
        {
            //Raise new OnNewSection event to notice the CarController that the car entered a new section
            OnNewSection();
        }

        /// <summary>
        /// For opening the COM port to the arduino board
        /// </summary>
        /// <returns>Port status open bool</returns>
        public bool OpenPort()
        {
            try
            {
                //Only open port when not open
                if (!_initialised && _serialPort.IsOpen == false)
                {
                    _serialPort.Open();
                }

            }
            catch (Exception ex)
            {
                ExceptionOccured(ex.Message);
                return _initialised;
            }
            return _initialised;
        }

        /// <summary>
        /// For closing the COM port to the arduino board
        /// </summary>
        /// <returns>Port status open bool</returns>
        public bool ClosePort()
        {
            //Only close port when open
            try
            {
                if (_initialised && _serialPort.IsOpen == true)
                {
                    _serialPort.Close();
                }
            }
            catch (Exception ex)
            {
                ExceptionOccured(ex.Message);
                return _initialised;
            }
            return _initialised = false;
        }

        /// <summary>
        /// Data received event for the COM port to the arduino board
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            //Get the data sent
            string data = _serialPort.ReadLine();
            string[] dataValues = data.Split(',');

            //Data is Sensor data
            if (dataValues.Count() == 6)
            {
                try
                {
                    //voltage
                    _voltage = Convert.ToDouble(dataValues[1], CultureInfo.InvariantCulture);

                    //amperage
                    _amperage = Convert.ToDouble(dataValues[2], CultureInfo.InvariantCulture);

                    _temperature = 0;
                    ////Temperature
                    //_temperature = Convert.ToDouble(dataValues[4], CultureInfo.InvariantCulture);
                    ////Check egnine overheated
                    //if (_temperature >= _maxTemperature)
                    //{
                    //    _overheat = true;
                    //}
                    //else
                    //{
                    //    _overheat = false;
                    //}

                    //Calculate wattage
                    _wattage = _wattSensor.Calculate(_voltage, _amperage);

                    //Calculate total distance
                    _distance = _speedSensor.TotalDistance;

                    //Send NewData event
                    EventNewData();
                }
                catch (Exception ex)
                {
                    ExceptionOccured(ex.Message);
                    return;
                }
            }

            //Data is WheelPulse data
            else if (dataValues.Count() == 3)
            {
                //Calculate speed
                try
                {
                    if (Convert.ToInt32(dataValues[1]) != -1)
                    {
                        _speedSensor.NewPulse(Convert.ToDouble(dataValues[1], CultureInfo.InvariantCulture));
                        _speed = _speedSensor.CalculateSpeed();
                    }

                    else
                    {
                        _speed = 0;
                    }
                }
                catch (Exception ex)
                {
                    ExceptionOccured(ex.Message);
                    return;
                }
            }
        }


        /// <summary>
        /// Returns total distance calculated by the wheel sensor
        /// </summary>
        /// <returns>The total distance</returns>
        public double GetTotalDistance()
        {
            return _speedSensor.TotalDistance;
        }
    }
}